package ar.edu.caece.compiler.grammar.attributes.tree;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ar.edu.caece.compiler.grammar.attributes.AttributeSymbolTable;
import ar.edu.caece.compiler.grammar.attributes.DeclaracionParametro;
import ar.edu.caece.compiler.grammar.attributes.DeclaracionTemporal;

public class NodeFuncProc extends AttributeTreeNode {

	private NodeDeclaraciones declaraciones;
	private String nombreProc;
	private NodeBloque bloque;
	private NodeExpr devolucion;
	private Type tipo;
	private String label;
	private AttributeSymbolTable tablaSimbolos;
	private List<DeclaracionParametro> parametros;
	
	private boolean isPrincipal;
	
	public NodeFuncProc(NodeDeclaraciones declaraciones, String nombreProc,
			NodeBloque bloque, NodeExpr devolucion) {
		super();
		this.declaraciones = declaraciones;
		this.nombreProc = nombreProc;
		this.bloque = bloque;
		this.devolucion = devolucion;
	}

	
	public void propagateFunction(NodeListaBloques raiz) {
		bloque.propagateRoot(raiz);
	}
	
	public void propagateRoot(NodeListaBloques raiz) {
		bloque.propagateRoot(raiz);		
	}
	
	public NodeFuncProc() {
		declaraciones = new NodeDeclaraciones();
		parametros = new ArrayList<DeclaracionParametro>();
	}


	public String getNombreProc() {
		return nombreProc;
	}
	public void setNombreProc(String nombreProc) {
		this.nombreProc = nombreProc;
	}

	public void setBloque(NodeBloque bloque) {
		this.bloque = bloque;
	}

	public NodeBloque getBloque() {
		return bloque;
	}

	public void setDevolucion(NodeExpr devolucion) {
		this.devolucion = devolucion;
	}

	public NodeExpr getDevolucion() {
		return devolucion;
	}

	public NodeDeclaraciones getDeclaraciones() {
		return declaraciones;
	}

	public void setDeclaraciones(NodeDeclaraciones declaraciones) {
		this.declaraciones = declaraciones;
	}

	public void setTipo(Type tipo) {
		this.tipo = tipo;
	}


	public Type getTipo() {
		return tipo;
	}

	public int cantidadVariablesLocales() {
		return getDeclaraciones().getCantidadVariablesLocales();
	}
	public int cantidadParametros() {
		return getDeclaraciones().getCantidadParametros();
	}
	
	@Override
	public String toString() {
		return "NodeFuncProc [parametros=" + parametros + ", bloque=" + bloque
				+ ", declaraciones=" + declaraciones + ", devolucion="
				+ devolucion + ", nombreProc=" + nombreProc + ", tipo=" + tipo
				+ " tablasimbolos:" + tablaSimbolos.toString() + "]";
	}

	public String generateDeclaration() {
		StringBuilder builder = new StringBuilder();

		for(NodeFuncProc bloque : declaraciones.getProcedimientos()) {
			if(!bloque.getLabel().contentEquals(label)) builder.append(bloque.generateDeclaration());	
		}
		
		builder.append(bloque.generateDeclaration());
		

		return builder.toString();
	}

	@Override
	public String generateCode() {
		int posRetorno;
		
		posRetorno = 10 +2*cantidadVariablesLocales()+8 + 2*(parametros==null?0:parametros.size());
		
		StringBuilder funcionBuilder = new StringBuilder();
		
		funcionBuilder.append(label) .append(" proc near").append(System.getProperty("line.separator") )
				.append(bloque.generateCode()).append(System.getProperty("line.separator") );
				if(devolucion!=null) {
					funcionBuilder.append(devolucion.generateCode()) 
					.append("mov [bp-" + posRetorno +"], cx").append(System.getProperty("line.separator"));
				}
				
				if(!isPrincipal) {
					funcionBuilder.append("ret").append(System.getProperty("line.separator"))
					.append(label).append(" endp").append( System.getProperty("line.separator"));
				} else {
					funcionBuilder.append("fin: ").append(System.getProperty("line.separator"))
					.append("mov ah,4Ch").append(System.getProperty("line.separator"))
					.append("int 21h").append(System.getProperty("line.separator"));
				}
					
		return funcionBuilder.toString();
	}


	public void setLabel(String label) {
		this.label = label;
	}
	
	public void propagarLabel() {
		declaraciones.setLabelPadre(label);
	}


	public String getLabel() {
		return label;
	}

	public NodeFuncProc getFuncProcFromLabel(String labelName) {
		if(label.contentEquals(labelName)) {
			return this;
		} else {
			NodeFuncProc bloque = null;
			Iterator<NodeFuncProc> nodeIterator = declaraciones.getProcedimientos().iterator();
			while(bloque==null && nodeIterator.hasNext()) {
				bloque = nodeIterator.next().getFuncProcFromLabel(labelName);
			}
			return bloque;
		}
	}

	public void setTablaSimbolos(AttributeSymbolTable tablaSimbolos) {
		this.tablaSimbolos = tablaSimbolos;
	}

	public AttributeSymbolTable getTablaSimbolos() {
		return tablaSimbolos;
	}
	
	public void propagarALlamadas() {
		bloque.propagarEntornoSuperior(this);
		if(devolucion!=null) devolucion.propagarEntornoSuperior(this);

	}

	public void setParametros(List<DeclaracionParametro> parametros) {
		this.parametros = parametros;
	}

	public List<DeclaracionParametro> getParametros() {
		return parametros;
	}
	
	public void agregarTemporales() {
		for(int i=0;i<5;i++) tablaSimbolos.addSymbolTableRow(new DeclaracionTemporal());
	}
	
	public int obtenerTemporal() {
		return tablaSimbolos.obtenerTemporal();
	}
	
	public void liberarTemporal() {
		tablaSimbolos.liberarTemporal();
	}

	public void prepararTemporales() {
		agregarTemporales();		
	}

	public void setPrincipal(boolean isPrincipal) {
		this.isPrincipal = isPrincipal;
	}

	public boolean isPrincipal() {
		return isPrincipal;
	}
}
